Skip to content

Conversation

@ericallam
Copy link
Member

@ericallam ericallam commented Jan 17, 2026

Summary

  • Store the original user-provided idempotency key and scope alongside the hash
  • Expose ctx.run.idempotencyKey as the user-provided key (not the hash)
  • Add ctx.run.idempotencyKeyScope to show the scope ("run", "attempt", or "global")
CleanShot 2026-01-19 at 11 40 46

Problem

Idempotency keys were hashed (SHA-256) before storage, making debugging difficult since users couldn't see the value they originally set or search for runs by idempotency key.

Solution

Attach metadata to the String object returned by idempotencyKeys.create() using a Symbol, extract it in the SDK before the API call, and store it in the database alongside the hash.

const key = await idempotencyKeys.create("my-key", { scope: "global" });
await childTask.triggerAndWait(payload, { idempotencyKey: key });

// In child task:
ctx.run.idempotencyKey      // "my-key" (previously showed the hash)
ctx.run.idempotencyKeyScope // "global"

Test plan

  • Trigger task with idempotencyKeys.create() using different scopes (run, attempt, global)
  • Verify ctx.run.idempotencyKey returns user-provided key
  • Verify ctx.run.idempotencyKeyScope returns correct scope
  • Verify PostgreSQL stores idempotencyKeyOptions JSON
  • Verify ClickHouse receives idempotency_key_user and idempotency_key_scope via replication

@changeset-bot
Copy link

changeset-bot bot commented Jan 17, 2026

🦋 Changeset detected

Latest commit: e3194fb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 27 packages
Name Type
@trigger.dev/sdk Patch
@trigger.dev/core Patch
@trigger.dev/python Patch
d3-chat Patch
references-d3-openai-agents Patch
references-nextjs-realtime Patch
references-realtime-hooks-test Patch
references-realtime-streams Patch
references-telemetry Patch
@trigger.dev/build Patch
trigger.dev Patch
@trigger.dev/redis-worker Patch
@trigger.dev/schema-to-json Patch
@internal/cache Patch
@internal/clickhouse Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/schedule-engine Patch
@internal/testcontainers Patch
@internal/tracing Patch
@internal/tsql Patch
@internal/zod-worker Patch
@trigger.dev/react-hooks Patch
@trigger.dev/rsc Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Warning

Rate limit exceeded

@ericallam has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 0 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 77cd398 and e3194fb.

📒 Files selected for processing (2)
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts

Walkthrough

Adds first-class support for user-provided idempotency key options throughout the system. Introduces a new optional JSON field TaskRun.idempotencyKeyOptions (Prisma + migration) and ClickHouse columns idempotency_key_user and idempotency_key_scope. Extends core idempotency APIs, catalog, and server-only helpers to register, extract, and reconstruct user keys and scopes. Threads idempotencyKeyOptions and user-facing idempotencyKey/scope through the run engine, attempt system, presenters, SDK batch/trigger plumbing, replication service, ClickHouse insert/columns, tests, and UI surfaces (including reset and run/span views).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: exposing user-provided idempotency key and scope in task context, which is the main objective across all files modified.
Description check ✅ Passed The description includes a clear summary of changes, explains the problem and solution, provides a code example, and outlines a comprehensive test plan covering all major functionality areas.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vibe-kanban-cloud
Copy link

Review Complete

Your review story is ready!

View Story

Comment !reviewfast on this PR to re-generate the story.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/webapp/app/services/runsReplicationService.server.ts`:
- Around line 894-895: The `#extractIdempotencyKeyScope` method currently returns
whatever is stored in idempotencyKeyOptions without validation; update it to
defensively validate the extracted scope is a string and one of the allowed
values "run" | "attempt" | "global" (matching the check used in
runAttemptSystem.ts) and if not, fall back to a safe default (e.g., "run") or
handle it the same way runAttemptSystem.ts does; locate and modify the
`#extractIdempotencyKeyScope` function to perform this whitelist check before
returning the scope.

In `@internal-packages/clickhouse/src/taskRuns.ts`:
- Around line 96-97: TASK_RUN_COLUMNS has its entries out of order causing
writes to map to wrong ClickHouse columns; update the TASK_RUN_COLUMNS array so
that columns added by migrations are appended in the actual table order: ensure
after the existing columns you place concurrency_key, bulk_action_group_ids,
worker_queue, max_duration_in_seconds, idempotency_key_user,
idempotency_key_scope (in that exact sequence) so the array order matches the
ClickHouse schema and insert mapping is correct.
🧹 Nitpick comments (1)
apps/webapp/app/presenters/v3/SpanPresenter.server.ts (1)

713-726: Consider adding Zod validation for the parsed JSON options.

The type assertion on run.idempotencyKeyOptions assumes the stored JSON matches the expected shape. If the data is malformed (e.g., from a migration issue or schema evolution), this could cause subtle runtime issues when accessing options.key.

As per coding guidelines, Zod should be used for validation in apps/webapp.

♻️ Suggested validation approach
+import { z } from "zod";
+
+const IdempotencyKeyOptionsSchema = z.object({
+  key: z.string().optional(),
+  scope: z.string().optional(),
+}).nullable();
+
 getUserProvidedIdempotencyKey(
   run: Pick<FindRunResult, "idempotencyKey" | "idempotencyKeyOptions">
 ): string | null {
-  const options = run.idempotencyKeyOptions as {
-    key?: string;
-    scope?: string;
-  } | null;
+  const parsed = IdempotencyKeyOptionsSchema.safeParse(run.idempotencyKeyOptions);
+  const options = parsed.success ? parsed.data : null;
   if (options?.key) {
     return options.key;
   }
   return run.idempotencyKey;
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 72594a4 and b6b0b67.

⛔ Files ignored due to path filters (1)
  • references/hello-world/src/trigger/idempotency.ts is excluded by !references/**
📒 Files selected for processing (15)
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
  • internal-packages/clickhouse/src/taskRuns.ts
  • internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/run-engine/src/engine/types.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/trigger-sdk/src/v3/shared.ts
🧰 Additional context used
📓 Path-based instructions (12)
internal-packages/database/prisma/migrations/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/database/prisma/migrations/**/*.sql: When editing the Prisma schema, remove extraneous migration lines related to specific tables: _BackgroundWorkerToBackgroundWorkerFile, _BackgroundWorkerToTaskQueue, _TaskRunToTaskRunTag, _WaitpointRunConnections, _completedWaitpoints, SecretStore_key_idx, and unrelated TaskRun indexes
Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file

Files:

  • internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
apps/webapp/app/services/**/*.server.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Separate testable services from configuration files; follow the pattern of realtimeClient.server.ts (testable service) and realtimeClientGlobal.server.ts (configuration) in the webapp

Files:

  • apps/webapp/app/services/runsReplicationService.server.ts
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
{packages,integrations}/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Add a changeset when modifying any public package in packages/* or integrations/* using pnpm run changeset:add

Files:

  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
internal-packages/clickhouse/schema/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/clickhouse/schema/**/*.sql: ClickHouse migrations must use Goose format with -- +goose Up and -- +goose Down markers
Follow ClickHouse naming conventions: raw_ prefix for input tables, _v1, _v2 suffixes for versioning, _mv_v1 suffix for materialized views

Files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
packages/trigger-sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Files:

  • packages/trigger-sdk/src/v3/shared.ts
🧠 Learnings (25)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes

Applied to files:

  • internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/clickhouse/src/taskRuns.ts
  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : Database indexes must use CONCURRENTLY to avoid table locks and must be in their own separate migration file

Applied to files:

  • internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/database/prisma/schema.prisma
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/clickhouse/src/taskRuns.ts
  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • packages/core/src/v3/schemas/common.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to **/*.{ts,tsx} : Every Trigger.dev task must be exported and have a unique `id` property with no timeouts in the run function

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/api.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • internal-packages/run-engine/src/engine/types.ts
  • packages/core/src/v3/schemas/common.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use metadata methods (set, del, replace, append, remove, increment, decrement, stream, flush) to update metadata during task execution

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `runs.subscribeToRunsWithTag()` to subscribe to all runs with a specific tag

Applied to files:

  • packages/core/src/v3/schemas/api.ts
📚 Learning: 2025-01-13T18:31:48.160Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 1608
File: apps/webapp/app/v3/services/triggerTask.server.ts:418-418
Timestamp: 2025-01-13T18:31:48.160Z
Learning: The `MachinePresetName` schema is used to validate machine preset values in the trigger.dev codebase, ensuring type safety and validation of machine preset options.

Applied to files:

  • packages/core/src/v3/schemas/api.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.trigger()` to trigger a task from inside another task with specified payload

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • apps/webapp/app/runEngine/services/triggerTask.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : Follow ClickHouse naming conventions: `raw_` prefix for input tables, `_v1`, `_v2` suffixes for versioning, `_mv_v1` suffix for materialized views

Applied to files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : ClickHouse migrations must use Goose format with `-- +goose Up` and `-- +goose Down` markers

Applied to files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
📚 Learning: 2025-07-12T18:06:04.133Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2264
File: apps/webapp/app/services/runsRepository.server.ts:172-174
Timestamp: 2025-07-12T18:06:04.133Z
Learning: In apps/webapp/app/services/runsRepository.server.ts, the in-memory status filtering after fetching runs from Prisma is intentionally used as a workaround for ClickHouse data delays. This approach is acceptable because the result set is limited to a maximum of 100 runs due to pagination, making the performance impact negligible.

Applied to files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
📚 Learning: 2025-08-14T12:13:20.455Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2392
File: packages/cli-v3/src/utilities/gitMeta.ts:195-218
Timestamp: 2025-08-14T12:13:20.455Z
Learning: In the GitMeta schema (packages/core/src/v3/schemas/common.ts), all fields are intentionally optional to handle partial data from various deployment contexts (local, GitHub Actions, GitHub App). Functions like getGitHubAppMeta() are designed to work with missing environment variables rather than validate their presence.

Applied to files:

  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
🧬 Code graph analysis (5)
apps/webapp/app/services/runsReplicationService.server.ts (2)
internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts (2)
  • run (1922-1928)
  • run (1930-1939)
packages/core/src/v3/schemas/common.ts (2)
  • TaskRun (209-237)
  • TaskRun (239-239)
apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts (3)
apps/webapp/app/presenters/v3/SpanPresenter.server.ts (4)
  • getUserProvidedIdempotencyKey (713-726)
  • run (608-614)
  • run (616-681)
  • run (683-685)
apps/webapp/app/services/runsReplicationService.server.ts (4)
  • run (846-905)
  • run (907-916)
  • run (957-960)
  • run (962-965)
internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts (2)
  • run (1922-1928)
  • run (1930-1939)
packages/core/src/v3/idempotencyKeys.ts (3)
packages/trigger-sdk/src/v3/idempotencyKeys.ts (1)
  • IdempotencyKey (8-8)
packages/core/src/v3/apiClient/index.ts (1)
  • resetIdempotencyKey (665-680)
packages/core/src/v3/apiClient/core.ts (1)
  • ZodFetchOptions (31-39)
internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts (4)
apps/webapp/app/presenters/v3/SpanPresenter.server.ts (3)
  • run (608-614)
  • run (616-681)
  • run (683-685)
apps/webapp/app/services/runsReplicationService.server.ts (4)
  • run (846-905)
  • run (907-916)
  • run (957-960)
  • run (962-965)
apps/webapp/app/v3/services/completeAttempt.server.ts (1)
  • run (419-517)
internal-packages/run-engine/src/run-queue/index.ts (1)
  • options (1126-1142)
packages/trigger-sdk/src/v3/shared.ts (1)
packages/core/src/v3/idempotencyKeys.ts (2)
  • makeIdempotencyKey (90-104)
  • getIdempotencyKeyOptions (29-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (39)
internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql (1)

1-2: LGTM — straightforward nullable JSONB column addition.

No extra indexes or extraneous lines introduced.

internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql (1)

1-16: LGTM — Goose markers and column additions look correct.

Defaults align with non-null String columns.

internal-packages/database/prisma/schema.prisma (1)

592-594: Good addition of idempotencyKeyOptions with clear doc comment.

This aligns with the new persistence layer for user-provided idempotency metadata.

internal-packages/run-engine/src/engine/types.ts (1)

127-128: Nice addition of idempotencyKeyOptions to TriggerParams.

This cleanly exposes the user-provided key and scope in the engine API.

internal-packages/run-engine/src/engine/index.ts (1)

398-399: LGTM — idempotencyKeyOptions is correctly threaded into TaskRun creation.

This ensures the new options are persisted alongside the run.

Also applies to: 548-548

apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts (3)

21-39: LGTM! Helper function correctly extracts user-provided idempotency key.

The implementation is consistent with the pattern used in SpanPresenter.server.ts. One minor observation: SpanPresenter's version returns string | null while this returns string | undefined. Both work correctly but the slight inconsistency could be unified in a future refactor.


61-61: LGTM!

Correctly adds idempotencyKeyOptions to the select object, enabling the helper function to access user-provided key options.


466-466: LGTM!

Correctly uses the helper to prefer user-provided idempotency keys over the stored hash, ensuring API consumers receive the original key they provided.

packages/core/src/v3/schemas/common.ts (2)

217-220: LGTM! Schema additions for idempotency key metadata.

The fields are correctly added as optional for backwards compatibility. The JSDoc comments clearly document the purpose. The scope enum values align with the IdempotencyKeyOptionsSchema defined in api.ts.


380-383: LGTM! Consistent schema extension in V3TaskRun.

The additions mirror the TaskRun schema changes, maintaining consistency across both schemas.

packages/core/src/v3/schemas/api.ts (4)

152-158: Schema design consideration: Both key and scope are required.

The schema requires both key and scope fields. This aligns with the design where idempotencyKeyOptions is only populated when the user explicitly provides both. If partial options should be supported (e.g., key without scope), consider making scope optional with a default.

Based on learnings, the scope parameter is used to "scope idempotency keys globally or to current run." The current required design seems intentional to ensure explicit scoping when user-provided keys are used.


202-203: LGTM!

Correctly adds idempotencyKeyOptions as an optional field to TriggerTaskRequestBody.options, allowing users to provide their original key and scope when triggering tasks.


253-254: LGTM!

Consistently adds idempotencyKeyOptions to BatchTriggerTaskItem.options, maintaining parity with single-task triggers.


360-361: LGTM!

Correctly adds idempotencyKeyOptions to CreateBatchRequestBody for the 2-phase batch API.

internal-packages/clickhouse/src/taskRuns.ts (2)

158-159: LGTM! Type definitions updated consistently.

Both TaskRunFieldTypes and TaskRunInsertArray are updated with the new string fields at the correct positions matching TASK_RUN_COLUMNS.

Also applies to: 291-292


43-44: LGTM! ClickHouse schema extension for idempotency key metadata.

The fields use default("") which aligns with the extraction helpers in runsReplicationService.server.ts that return empty strings when options are missing. The ClickHouse migration (013_add_task_runs_v2_idempotency_key_options.sql) exists with matching column definitions and proper Goose format, and all four schema locations in taskRuns.ts are updated consistently with correct column ordering.

apps/webapp/app/runEngine/services/triggerTask.server.ts (1)

307-307: LGTM!

Correctly propagates idempotencyKeyOptions from the request body to the engine's trigger method, enabling the run engine to persist the user-provided key and scope.

internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts (5)

197-197: LGTM!

Correctly adds idempotencyKeyOptions to the Prisma select to enable the helper functions.


265-266: LGTM!

Correctly populates idempotencyKey and idempotencyKeyScope in the TaskRunContext using the new helper methods.


427-427: LGTM!

Correctly includes idempotencyKeyOptions in the update query's select clause.


576-577: LGTM!

Correctly applies the helpers to populate idempotency key fields in the execution context returned to workers.


1921-1939: LGTM! Well-implemented helper methods.

The private helpers follow the same pattern established across the codebase:

  1. #getUserProvidedIdempotencyKey correctly returns user-provided key if available, falling back to the hash
  2. #getIdempotencyKeyScope properly validates that scope is one of the allowed values before returning

The explicit scope validation on lines 1935-1936 is a good defensive check that ensures only valid enum values propagate to consumers.

apps/webapp/app/presenters/v3/SpanPresenter.server.ts (2)

232-232: LGTM!

The refactoring to use getUserProvidedIdempotencyKey properly prioritizes user-provided keys over stored hashes, which aligns with the PR objective.

Also applies to: 648-648


358-358: LGTM!

The addition of idempotencyKeyOptions to the select query ensures the field is available for the helper method.

packages/trigger-sdk/src/v3/shared.ts (10)

14-14: LGTM!

Import of getIdempotencyKeyOptions is correctly added to support the new feature.


2118-2135: LGTM!

The idempotency key processing in trigger_internal correctly extracts options and passes both the stringified key and options to the API.


2065-2094: Inconsistent idempotencyKeyOptions propagation across transform functions.

This function correctly extracts and propagates idempotencyKeyOptions, but the other five similar transform functions do not:

  • transformBatchItemsStream (lines 1791-1835)
  • transformBatchItemsStreamForWait (lines 1843-1887)
  • transformBatchByTaskItemsStream (lines 1894-1938)
  • transformBatchByTaskItemsStreamForWait (lines 1945-1989)
  • transformSingleTaskBatchItemsStream (lines 1996-2042)

This means user-provided idempotency key options won't be preserved when using streaming batches via batchTriggerById, batchTriggerByIdAndWait, batchTriggerTasks, or batchTriggerAndWaitTasks.

Is this intentional, or should the same pattern be applied to all transform functions for consistency?


630-654: Similar inconsistency in array-based batch paths.

The public batchTriggerById, batchTriggerByIdAndWait, batchTriggerTasks, and batchTriggerAndWaitTasks functions don't extract idempotencyKeyOptions from individual items in their array paths, while batchTrigger_internal and batchTriggerAndWait_internal do.

This creates an inconsistent user experience where the same feature works for some batch APIs but not others.

Also applies to: 886-910, 1146-1170, 1407-1430


1544-1545: LGTM!

The executeBatchTwoPhase options type correctly includes idempotencyKeyOptions with the proper scope union type.

Also applies to: 1559-1560


2239-2254: LGTM!

The batch-level idempotency key options are correctly processed and passed to executeBatchTwoPhase for the array path.


2301-2316: LGTM!

The streaming path correctly extracts and passes batch-level idempotency key options to executeBatchTwoPhaseStreaming.


2374-2403: LGTM!

The triggerAndWait_internal correctly processes the idempotency key and extracts options for storage.


2512-2529: LGTM!

The batch-level idempotency key processing in batchTriggerAndWait_internal array path is correct.


2581-2598: LGTM!

The streaming path in batchTriggerAndWait_internal correctly handles batch-level idempotency key options.

packages/core/src/v3/idempotencyKeys.ts (5)

7-12: LGTM!

New types correctly define the idempotency key scope and options structure. Using type instead of interface aligns with coding guidelines.


14-46: LGTM!

The symbol-based metadata attachment pattern is well-implemented:

  • Uses Symbol.for() for consistent cross-realm identity
  • Creates String objects via new String() to allow property attachment while maintaining string semantics
  • Internal helper is properly marked with @internal JSDoc

48-61: LGTM!

The updated isIdempotencyKey correctly handles both primitive strings and String objects created by attachIdempotencyKeyOptions.


141-153: LGTM!

The createIdempotencyKey function now correctly attaches the original user key and scope as metadata, enabling downstream extraction via getIdempotencyKeyOptions.


192-279: LGTM!

The resetIdempotencyKey function handles multiple input formats correctly:

  • Direct 64-char hashes pass through efficiently
  • IdempotencyKey objects with attached options have their metadata extracted
  • Raw strings/arrays are hashed with proper scope handling
  • Clear error messages when required context is missing outside task runs

The documentation is thorough with good examples.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@ericallam
Copy link
Member Author

@claude could you review this PR please?

@claude
Copy link

claude bot commented Jan 17, 2026

Claude finished @ericallam's task in 2m 14s —— View job


PR Review: feat(sdk): expose user-provided idempotency key and scope in task context


Overview

This PR solves an important usability problem: previously, idempotency keys were hashed (SHA-256) before storage, making debugging difficult since users couldn't see the value they originally set. The solution elegantly stores the original key and scope alongside the hash, exposing them via ctx.run.idempotencyKey and ctx.run.idempotencyKeyScope.


Architecture Assessment ✅

The implementation approach is well-designed:

  1. Symbol-based metadata attachment (packages/core/src/v3/idempotencyKeys.ts:14-46): Using Symbol.for() to attach metadata to String objects is a clever pattern that:

    • Maintains backward compatibility (the string still works as a string)
    • Allows extraction before API calls without modifying the core flow
    • Cross-realm symbol identity via Symbol.for() is the right choice
  2. Clean separation of concerns:

    • packages/core/src/v3/serverOnly/idempotencyKeys.ts: Server-side helpers with Zod validation via safeParse
    • packages/core/src/v3/idempotencyKeys.ts: SDK-side creation and extraction

Strengths 💪

  1. Consistent helper usage across the codebase:

    • runAttemptSystem.ts:269-270 - Uses getUserProvidedIdempotencyKey() and getIdempotencyKeyScope()
    • runsReplicationService.server.ts:895-896 - Uses extractIdempotencyKeyUser() and getIdempotencyKeyScope()
    • SpanPresenter.server.ts:233 and ApiRetrieveRunPresenter.server.ts:466 - Uses getUserProvidedIdempotencyKey()
  2. Proper Zod validation in server-only helpers (packages/core/src/v3/serverOnly/idempotencyKeys.ts:14, 30, 47) - uses safeParse to gracefully handle malformed data

  3. ClickHouse migration (internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql) follows the Goose format correctly with proper -- +goose Up and -- +goose Down markers

  4. Prisma migration is minimal and correct - just adds a nullable JSONB column

  5. Test coverage - The ClickHouse test (internal-packages/clickhouse/src/taskRuns.test.ts:67-68) includes the new fields in the test data

  6. Schema consistency - Both TaskRun and V3TaskRun schemas in packages/core/src/v3/schemas/common.ts:217-220, 380-383 properly add the new optional fields with good JSDoc comments


Minor Observations 📝

  1. Type assertion in SpanPresenter (apps/webapp/app/presenters/v3/SpanPresenter.server.ts - the getUserProvidedIdempotencyKey helper there): While it works, the inline type assertion run.idempotencyKeyOptions as { key?: string; scope?: string } | null could benefit from the same safeParse validation used in the server-only helpers for consistency. However, this is low risk since the data source is Prisma/DB which was validated on write.

  2. The resetIdempotencyKey function (packages/core/src/v3/idempotencyKeys.ts:226-280) is a nice addition that handles all the edge cases well - extracting options from IdempotencyKey objects, handling raw strings, and properly reconstructing hashes with scope suffixes.


Summary

This is a well-implemented feature that:

  • Solves a real debugging pain point
  • Uses elegant patterns (Symbol-based metadata)
  • Maintains backward compatibility
  • Has proper validation at server boundaries
  • Includes appropriate test coverage

The code is clean, well-organized, and follows the repository's patterns. LGTM 👍


| Branch: feat/expose-user-idempotency-key-in-context

@ericallam ericallam marked this pull request as ready for review January 17, 2026 12:29
@matt-aitken
Copy link
Member

@ericallam on the run page can we update the "Details" panel? If the user idempotency key is set show that, and the scope

@ericallam ericallam force-pushed the feat/expose-user-idempotency-key-in-context branch from c21d103 to 9b431a0 Compare January 19, 2026 17:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/trigger-sdk/src/v3/shared.ts (1)

2065-2095: Apply idempotencyKeyOptions fallback to per-item batch runs when item-level keys are absent.

When only a batch-level idempotencyKey is provided, per-item runs currently skip idempotencyKeyOptions, losing scope information. Although finalIdempotencyKey correctly falls back to the batch-derived key, idempotencyKeyOptions becomes undefined, causing the server to lack the scope needed by getIdempotencyKeyScope().

Apply the fallback pattern consistently across all three batch locations (lines 2065–2095, 2200–2229, 2474–2504):

Fallback pattern
    const itemIdempotencyKey = await makeIdempotencyKey(item.options?.idempotencyKey);
    const finalIdempotencyKey = itemIdempotencyKey ?? batchItemIdempotencyKey;
+   const batchLevelIdempotencyKey = batchItemIdempotencyKey;
    const idempotencyKeyOptions = itemIdempotencyKey
      ? getIdempotencyKeyOptions(itemIdempotencyKey)
+     : batchLevelIdempotencyKey
+     ? getIdempotencyKeyOptions(batchLevelIdempotencyKey)
      : undefined;
🤖 Fix all issues with AI agents
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx:
- Around line 85-90: The error handling currently interpolates
JSON.stringify(error) which can throw for circular/non-serializable payloads;
update the handler that calls logger.error and jsonWithErrorMessage so you
safely convert the error before building the message: replace
JSON.stringify(error) with a safe stringify routine (e.g., try {
JSON.stringify(error) } catch { fallback to util.inspect(error) or String(error)
}) or use a helper like safeStringify, and pass that sanitized string into
jsonWithErrorMessage while keeping the logger.error call (logger.error("Failed
to reset idempotency key", { error })) unchanged.

In `@packages/core/src/v3/idempotencyKeys.ts`:
- Around line 7-46: The current implementation attaches options to String
objects via IDEMPOTENCY_KEY_OPTIONS_SYMBOL and returns String instances from
idempotencyKeys.create(), which breaks primitive string typing and symbol
persistence; change the approach to always return primitive strings and replace
attachIdempotencyKeyOptions/getIdempotencyKeyOptions logic with a module-scoped
Map<string, IdempotencyKeyOptions> keyed by the string hash (store options in
the Map when creating keys and lookup by the primitive string in
getIdempotencyKeyOptions and resetIdempotencyKey), remove reliance on new
String(...) and IDEMPOTENCY_KEY_OPTIONS_SYMBOL, and update resetIdempotencyKey
and any callers to use primitive string checks (typeof === "string") and Map
lookups for options.
🧹 Nitpick comments (1)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (1)

9-12: Handle invalid route params explicitly.
parse will throw on bad params and skip the action’s error handling. Consider safeParse to return a friendly message instead.

Suggested change
-  const { projectParam, organizationSlug, envParam, runParam } = v3RunParamsSchema.parse(params);
+  const parsedParams = v3RunParamsSchema.safeParse(params);
+  if (!parsedParams.success) {
+    return jsonWithErrorMessage({}, request, "Invalid run parameters");
+  }
+  const { projectParam, organizationSlug, envParam, runParam } = parsedParams.data;
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c21d103 and 9b431a0.

⛔ Files ignored due to path filters (1)
  • references/hello-world/src/trigger/idempotency.ts is excluded by !references/**
📒 Files selected for processing (21)
  • .changeset/bright-keys-shine.md
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
  • internal-packages/clickhouse/src/taskRuns.test.ts
  • internal-packages/clickhouse/src/taskRuns.ts
  • internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql
  • internal-packages/database/prisma/schema.prisma
  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/run-engine/src/engine/types.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/common.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
  • packages/core/src/v3/serverOnly/index.ts
  • packages/trigger-sdk/src/v3/shared.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • .changeset/bright-keys-shine.md
  • internal-packages/clickhouse/src/taskRuns.ts
  • internal-packages/database/prisma/migrations/20260116154810_add_idempotency_key_options_to_task_run/migration.sql
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/schemas/common.ts
  • internal-packages/database/prisma/schema.prisma
  • apps/webapp/app/runEngine/services/triggerTask.server.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • packages/core/src/v3/serverOnly/index.ts
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • internal-packages/clickhouse/src/taskRuns.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • internal-packages/clickhouse/src/taskRuns.test.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/clickhouse/src/taskRuns.test.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • internal-packages/clickhouse/src/taskRuns.test.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
{packages,integrations}/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Add a changeset when modifying any public package in packages/* or integrations/* using pnpm run changeset:add

Files:

  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
packages/trigger-sdk/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Files:

  • packages/trigger-sdk/src/v3/shared.ts
internal-packages/clickhouse/schema/**/*.sql

📄 CodeRabbit inference engine (CLAUDE.md)

internal-packages/clickhouse/schema/**/*.sql: ClickHouse migrations must use Goose format with -- +goose Up and -- +goose Down markers
Follow ClickHouse naming conventions: raw_ prefix for input tables, _v1, _v2 suffixes for versioning, _mv_v1 suffix for materialized views

Files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use vitest for all tests in the Trigger.dev repository

Files:

  • internal-packages/clickhouse/src/taskRuns.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptive describe and it blocks
Tests should avoid mocks or stubs and use the helpers from @internal/testcontainers when Redis or Postgres are needed
Use vitest for running unit tests

**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g., MyService.ts) → MyService.test.ts

Files:

  • internal-packages/clickhouse/src/taskRuns.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers helpers (redisTest, postgresTest, containerTest) from @internal/testcontainers for Redis/PostgreSQL testing instead of mocks

Files:

  • internal-packages/clickhouse/src/taskRuns.test.ts
🧠 Learnings (28)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • internal-packages/clickhouse/src/taskRuns.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • internal-packages/clickhouse/src/taskRuns.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/clickhouse/src/taskRuns.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/clickhouse/src/taskRuns.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to **/*.{ts,tsx} : Every Trigger.dev task must be exported and have a unique `id` property with no timeouts in the run function

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • internal-packages/clickhouse/src/taskRuns.test.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Subscribe to run updates using `runs.subscribeToRun()` for realtime monitoring of task execution

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.batchTrigger()` to trigger multiple runs of a single task with different payloads

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • internal-packages/run-engine/src/engine/index.ts
  • internal-packages/run-engine/src/engine/types.ts
  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • internal-packages/run-engine/src/engine/types.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/v3/presenters/**/*.server.{ts,tsx} : Organize presenters in the webapp following the pattern `app/v3/presenters/*/*.server.ts` to move complex loader code into classes

Applied to files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks

Applied to files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-10-08T11:48:12.327Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 2593
File: packages/core/src/v3/workers/warmStartClient.ts:168-170
Timestamp: 2025-10-08T11:48:12.327Z
Learning: The trigger.dev runners execute only in Node 21 and 22 environments, so modern Node.js APIs like AbortSignal.any (introduced in v20.3.0) are supported.

Applied to files:

  • apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/trigger-sdk/src/v3/shared.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to **/*.{ts,tsx} : Always import tasks from `trigger.dev/sdk`, never use `trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern

Applied to files:

  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use metadata methods (set, del, replace, append, remove, increment, decrement, stream, flush) to update metadata during task execution

Applied to files:

  • packages/trigger-sdk/src/v3/shared.ts
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Use the Run Engine 2.0 from `internal/run-engine` for new run lifecycle code in the webapp instead of the legacy run engine

Applied to files:

  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2025-07-12T18:06:04.133Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2264
File: apps/webapp/app/services/runsRepository.server.ts:172-174
Timestamp: 2025-07-12T18:06:04.133Z
Learning: In apps/webapp/app/services/runsRepository.server.ts, the in-memory status filtering after fetching runs from Prisma is intentionally used as a workaround for ClickHouse data delays. This approach is acceptable because the result set is limited to a maximum of 100 runs due to pagination, making the performance impact negligible.

Applied to files:

  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2025-08-14T12:13:20.455Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2392
File: packages/cli-v3/src/utilities/gitMeta.ts:195-218
Timestamp: 2025-08-14T12:13:20.455Z
Learning: In the GitMeta schema (packages/core/src/v3/schemas/common.ts), all fields are intentionally optional to handle partial data from various deployment contexts (local, GitHub Actions, GitHub App). Functions like getGitHubAppMeta() are designed to work with missing environment variables rather than validate their presence.

Applied to files:

  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/database/prisma/migrations/**/*.sql : When editing the Prisma schema, remove extraneous migration lines related to specific tables: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated `TaskRun` indexes

Applied to files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : Follow ClickHouse naming conventions: `raw_` prefix for input tables, `_v1`, `_v2` suffixes for versioning, `_mv_v1` suffix for materialized views

Applied to files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to internal-packages/clickhouse/schema/**/*.sql : ClickHouse migrations must use Goose format with `-- +goose Up` and `-- +goose Down` markers

Applied to files:

  • internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql
🧬 Code graph analysis (6)
apps/webapp/app/presenters/v3/SpanPresenter.server.ts (1)
packages/core/src/v3/serverOnly/idempotencyKeys.ts (2)
  • getUserProvidedIdempotencyKey (10-19)
  • getIdempotencyKeyScope (27-35)
packages/core/src/v3/idempotencyKeys.ts (3)
packages/trigger-sdk/src/v3/idempotencyKeys.ts (1)
  • IdempotencyKey (8-8)
packages/core/src/v3/apiClient/index.ts (1)
  • resetIdempotencyKey (665-680)
packages/core/src/v3/apiClient/core.ts (1)
  • ZodFetchOptions (31-39)
packages/trigger-sdk/src/v3/shared.ts (1)
packages/core/src/v3/idempotencyKeys.ts (2)
  • makeIdempotencyKey (90-104)
  • getIdempotencyKeyOptions (29-33)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (4)
apps/webapp/app/services/session.server.ts (1)
  • requireUserId (25-35)
apps/webapp/app/utils/pathBuilder.ts (1)
  • v3RunParamsSchema (35-37)
apps/webapp/app/models/message.server.ts (2)
  • jsonWithErrorMessage (125-142)
  • jsonWithSuccessMessage (106-123)
apps/webapp/app/v3/services/resetIdempotencyKey.server.ts (1)
  • ResetIdempotencyKeyService (5-36)
internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts (1)
packages/core/src/v3/serverOnly/idempotencyKeys.ts (2)
  • getUserProvidedIdempotencyKey (10-19)
  • getIdempotencyKeyScope (27-35)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (3)
apps/webapp/app/components/primitives/CopyableText.tsx (1)
  • CopyableText (8-98)
apps/webapp/app/components/primitives/Tooltip.tsx (1)
  • InfoIconTooltip (110-139)
apps/webapp/app/utils/pathBuilder.ts (2)
  • docsPath (549-551)
  • v3RunIdempotencyKeyResetPath (344-353)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (20)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (5)

1-9: LGTM!

Icon imports are correctly added and both BookOpenIcon and ClockIcon are used in the new idempotency UI section.


164-165: LGTM!

Good UX improvement. Showing existing data during revalidation instead of a loading spinner prevents UI flickering and provides a smoother experience.


423-428: LGTM!

Run ID display follows the existing pattern for copyable IDs and provides a convenient way for users to access the run identifier.


567-624: Well-structured idempotency info section.

The tooltip provides comprehensive documentation for users with clear explanations of scope and status values. The conditional status display handles all states appropriately, and the docs link follows the established pattern using docsPath().

One minor observation: the label content is quite complex with the nested flex containers, tooltip, and conditional actions. Consider extracting the tooltip content into a separate component if this pattern needs reuse elsewhere.


625-658: Properly implements the requested UI for idempotency details.

The display correctly surfaces the user-provided key, scope, and expiration as requested in the PR comments. The conditional "Expired/Expires" label based on status is a nice UX touch. All four idempotency properties (idempotencyKey, idempotencyKeyScope, idempotencyKeyExpiresAt, idempotencyKeyStatus) are correctly included in the SpanRun type and properly accessed.

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.idempotencyKey.reset.tsx (1)

3-3: Nice improvement: standardized response helpers.
Using the shared message helpers here keeps toast/session handling consistent across outcomes.

Also applies to: 41-46, 62-73

apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts (1)

12-12: LGTM for the idempotency key exposure update.

Also applies to: 41-43, 444-448

internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts (1)

33-36: LGTM for propagating user idempotency key and scope through context/execution payloads.

Also applies to: 200-202, 269-271, 430-432, 580-582

internal-packages/run-engine/src/engine/types.ts (1)

121-129: LGTM.

internal-packages/run-engine/src/engine/index.ts (1)

392-399: LGTM for persisting idempotencyKeyOptions on run creation.

Also applies to: 546-549

apps/webapp/app/presenters/v3/SpanPresenter.server.ts (1)

11-14: LGTM for surfacing user idempotency key, scope, and status in span/run payloads.

Also applies to: 236-240, 285-307, 386-389, 678-679

packages/core/src/v3/idempotencyKeys.ts (3)

48-60: LGTM.


141-153: LGTM.

Also applies to: 155-156


192-279: LGTM.

packages/trigger-sdk/src/v3/shared.ts (3)

14-14: Nice: user idempotency options are preserved for trigger + triggerAndWait.

Extracting options before stringifying the key keeps the original key/scope available while still sending the normalized key value.

Also applies to: 2118-2136, 2374-2404


1541-1560: Good: batch creation now carries idempotencyKeyOptions across both paths.

This keeps batch-level metadata consistent for array and streaming execution.

Also applies to: 1699-1703


2239-2255: LGTM: batch-level idempotencyKeyOptions are forwarded to createBatch.

Covers both array and streaming paths for batch trigger + batch trigger-and-wait.

Also applies to: 2300-2317, 2512-2530, 2581-2599

internal-packages/clickhouse/schema/013_add_task_runs_v2_idempotency_key_options.sql (1)

1-16: LGTM: clean Goose migration for user key + scope columns.

Up/Down sections are consistent and reversible.

internal-packages/clickhouse/src/taskRuns.test.ts (2)

66-68: LGTM: fixture includes the new idempotency key user/scope fields.


193-195: Good: additional test payloads updated for the new columns.

Also applies to: 243-245, 341-342

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (1)

993-1052: Add an accessible label to the icon-only logs menu button.
Screen readers won’t announce this control without a label.

✅ Suggested fix
               <PopoverTrigger asChild>
                 <Button
                   variant="secondary/medium"
                   className="rounded-l-none border-l-charcoal-700 px-1.5"
+                  aria-label="Logs actions"
+                  title="Logs actions"
                 >
                   <ChevronUpIcon className="size-4 transition group-hover/button:text-text-bright" />
                 </Button>
               </PopoverTrigger>
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e076708 and 0487cae.

📒 Files selected for processing (1)
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (7)

1-43: Imports updated for new UI elements.
The added icons and Popover components align with the new UI affordances.


77-80: Session guard import wired in.
Keeps loader auth enforcement explicit.


98-100: Route/action imports look consistent.
No concerns with the added action + waitpoint form imports.


173-175: Spinner now only shows on first load.
Nice reduction of revalidation flicker.


324-324: RunBody grid rows adjusted for the new footer.
Layout change looks fine.


432-437: Run ID surfaced in details.
The copyable value is a helpful addition.


576-686: Idempotency details + status UI is clear.
Key/scope/expiry visibility and reset behavior are well surfaced.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

…potency key user supplied key and options instead of using a String object so we don't break the contract of idempotencyKeys.create
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts`:
- Around line 49-64: The test stops before adding the 4th key and asserting
eviction; after the initial registerKeyOptions("hash1"/"hash2"/"hash3") and the
getKeyOptions accesses, add catalog.registerKeyOptions("hash4", { key: "key4",
scope: "global" }) and then assert that getKeyOptions("hash1") is undefined
(evicted) while getKeyOptions("hash2") and getKeyOptions("hash3") are still
defined to validate LRU eviction in LRUIdempotencyKeyCatalog.

In `@packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts`:
- Around line 7-23: The eviction loop in registerKeyOptions can run forever if
maxSize is negative; clamp or validate maxSize to be non-negative. Update the
constructor (LRUIdempotencyKeyCatalog constructor) to enforce this.maxSize =
Math.max(0, maxSize) (or throw on negative), and/or change the eviction
condition in registerKeyOptions to while (this.cache.size > Math.max(0,
this.maxSize)) so the loop terminates; reference the constructor, this.maxSize,
this.cache, and registerKeyOptions when making the change.
🧹 Nitpick comments (1)
packages/core/src/v3/idempotency-key-catalog/index.ts (1)

1-5: Consider moving the constant after imports.

The API_NAME constant is defined before the imports, which is unconventional. While valid, imports are typically grouped at the top of the file for readability.

Suggested reordering
+import { getGlobal, registerGlobal } from "../utils/globals.js";
+import type { IdempotencyKeyCatalog, IdempotencyKeyOptions } from "./catalog.js";
+import { LRUIdempotencyKeyCatalog } from "./lruIdempotencyKeyCatalog.js";
+
 const API_NAME = "idempotency-key-catalog";
-
-import { getGlobal, registerGlobal } from "../utils/globals.js";
-import type { IdempotencyKeyCatalog, IdempotencyKeyOptions } from "./catalog.js";
-import { LRUIdempotencyKeyCatalog } from "./lruIdempotencyKeyCatalog.js";
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0487cae and 77cd398.

📒 Files selected for processing (12)
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
  • packages/core/src/v3/utils/globals.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal-packages/run-engine/src/engine/systems/runAttemptSystem.ts
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: Always import tasks from @trigger.dev/sdk, never use @trigger.dev/sdk/v3 or deprecated client.defineJob pattern
Every Trigger.dev task must be exported and have a unique id property with no timeouts in the run function

Files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

Import from @trigger.dev/core using subpaths only, never import from root

Files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

**/*.ts: When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
Do not use high-cardinality attributes in OTEL metrics such as UUIDs/IDs (envId, userId, runId, projectId, organizationId), unbounded integers (itemCount, batchSize, retryCount), timestamps (createdAt, startTime), or free-form strings (errorMessage, taskName, queueName)
When exporting OTEL metrics via OTLP to Prometheus, be aware that the exporter automatically adds unit suffixes to metric names (e.g., 'my_duration_ms' becomes 'my_duration_ms_milliseconds', 'my_counter' becomes 'my_counter_total'). Account for these transformations when writing Grafana dashboards or Prometheus queries

Files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
{packages,integrations}/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Add a changeset when modifying any public package in packages/* or integrations/* using pnpm run changeset:add

Files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/idempotency-key-catalog/index.ts
  • packages/core/src/v3/utils/globals.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
  • packages/core/src/v3/idempotency-key-catalog-api.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use vitest for all tests in the Trigger.dev repository

Files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
apps/webapp/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Test files should only import classes and functions from app/**/*.ts files and should not import env.server.ts directly or indirectly; pass configuration through options instead

For testable code in the webapp, never import env.server.ts in test files - pass configuration as options instead

Files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Access environment variables via env export from apps/webapp/app/env.server.ts, never use process.env directly

Files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx,js,jsx}: Test files should live beside the files under test and use descriptive describe and it blocks
Tests should avoid mocks or stubs and use the helpers from @internal/testcontainers when Redis or Postgres are needed
Use vitest for running unit tests

**/*.test.{ts,tsx,js,jsx}: Use vitest exclusively for testing and never mock anything - use testcontainers instead
Place test files next to source files with naming pattern: source file (e.g., MyService.ts) → MyService.test.ts

Files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use testcontainers helpers (redisTest, postgresTest, containerTest) from @internal/testcontainers for Redis/PostgreSQL testing instead of mocks

Files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
  • packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
apps/webapp/app/services/**/*.server.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Separate testable services from configuration files; follow the pattern of realtimeClient.server.ts (testable service) and realtimeClientGlobal.server.ts (configuration) in the webapp

Files:

  • apps/webapp/app/services/runsReplicationService.server.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Scope idempotency keys globally or to current run using the scope parameter

Applied to files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeys.create()` to create idempotency keys for preventing duplicate task executions

Applied to files:

  • packages/core/src/v3/idempotency-key-catalog/catalog.ts
  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `idempotencyKeyTTL` option to define a time window during which duplicate triggers return the original run

Applied to files:

  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/test/runsReplicationService.part2.test.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
  • packages/core/src/v3/idempotencyKeys.ts
  • packages/core/src/v3/serverOnly/idempotencyKeys.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to **/*.{ts,tsx} : Always import tasks from `trigger.dev/sdk`, never use `trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern

Applied to files:

  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to **/*.{ts,tsx} : Every Trigger.dev task must be exported and have a unique `id` property with no timeouts in the run function

Applied to files:

  • packages/core/src/v3/utils/globals.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : When importing from `trigger.dev/core` in the webapp, use subpath exports from the package.json instead of importing from the root path

Applied to files:

  • packages/core/src/v3/utils/globals.ts
📚 Learning: 2026-01-15T11:50:06.044Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.044Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Import from `trigger.dev/core` using subpaths only, never import from root

Applied to files:

  • packages/core/src/v3/utils/globals.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Specify machine resources using the `machine` property with preset options like 'large-1x'

Applied to files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
📚 Learning: 2025-07-12T18:06:04.133Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2264
File: apps/webapp/app/services/runsRepository.server.ts:172-174
Timestamp: 2025-07-12T18:06:04.133Z
Learning: In apps/webapp/app/services/runsRepository.server.ts, the in-memory status filtering after fetching runs from Prisma is intentionally used as a workaround for ClickHouse data delays. This approach is acceptable because the result set is limited to a maximum of 100 runs due to pagination, making the performance impact negligible.

Applied to files:

  • apps/webapp/test/runsReplicationService.part2.test.ts
  • apps/webapp/app/services/runsReplicationService.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Attach metadata to task runs using the metadata option when triggering, and access/update it inside runs using metadata functions

Applied to files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Subscribe to run updates using `runs.subscribeToRun()` for realtime monitoring of task execution

Applied to files:

  • apps/webapp/app/services/runsReplicationService.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks

Applied to files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • apps/webapp/app/services/runsReplicationService.server.ts
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • apps/webapp/app/services/runsReplicationService.server.ts
  • apps/webapp/app/presenters/v3/SpanPresenter.server.ts
🧬 Code graph analysis (7)
packages/core/src/v3/utils/globals.ts (2)
packages/core/src/v3/idempotency-key-catalog/catalog.ts (1)
  • IdempotencyKeyCatalog (8-11)
packages/core/src/v3/idempotency-key-catalog/index.ts (1)
  • IdempotencyKeyCatalog (27-35)
packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts (1)
packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts (1)
  • LRUIdempotencyKeyCatalog (3-35)
packages/core/src/v3/idempotency-key-catalog-api.ts (1)
packages/core/src/v3/idempotency-key-catalog/index.ts (1)
  • IdempotencyKeyCatalogAPI (7-36)
apps/webapp/app/services/runsReplicationService.server.ts (3)
packages/core/src/v3/serverOnly/idempotencyKeys.ts (2)
  • unsafeExtractIdempotencyKeyUser (61-67)
  • unsafeExtractIdempotencyKeyScope (37-42)
apps/webapp/app/presenters/v3/SpanPresenter.server.ts (3)
  • run (638-644)
  • run (646-711)
  • run (713-715)
apps/webapp/app/v3/services/finalizeTaskRun.server.ts (1)
  • run (191-262)
apps/webapp/app/presenters/v3/SpanPresenter.server.ts (1)
packages/core/src/v3/serverOnly/idempotencyKeys.ts (2)
  • getUserProvidedIdempotencyKey (10-19)
  • extractIdempotencyKeyScope (27-35)
packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts (3)
packages/core/src/v3/idempotency-key-catalog/catalog.ts (2)
  • IdempotencyKeyCatalog (8-11)
  • IdempotencyKeyOptions (3-6)
packages/core/src/v3/idempotency-key-catalog/index.ts (1)
  • IdempotencyKeyCatalog (27-35)
packages/core/src/v3/idempotencyKeys.ts (1)
  • IdempotencyKeyOptions (13-13)
packages/core/src/v3/serverOnly/idempotencyKeys.ts (1)
apps/webapp/app/services/runsReplicationService.server.ts (2)
  • run (847-906)
  • run (908-917)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (37)
packages/core/src/v3/idempotencyKeys.ts (8)

1-13: LGTM!

Clean migration to catalog-based approach with proper re-exports for backwards compatibility. The separation of concerns between the idempotency key API and the catalog implementation is well structured.


28-36: LGTM!

The catalog lookup approach is clean. The return undefined fallback at line 35 is defensive since IdempotencyKey is a branded string that would pass the typeof check, but it's reasonable to keep for runtime safety against invalid inputs.


38-47: LGTM!

The 64-character length check correctly identifies SHA-256 hex digests, and the updated comment accurately reflects the move away from String objects.


76-90: LGTM!

The function correctly delegates to createIdempotencyKey with the default "run" scope while preserving already-hashed keys.


127-142: LGTM!

The implementation correctly:

  1. Registers the original user-provided key and scope in the catalog before returning
  2. Returns a primitive string (addressing the past review concerns about String objects)
  3. Maintains consistency with the hash generation algorithm

Based on learnings, this properly supports scoping idempotency keys globally or to the current run using the scope parameter.


144-161: LGTM!

The function correctly maps each scope to its corresponding key material, with a safe fallback to empty array when task context is unavailable.


181-187: LGTM!

The type definition follows coding guidelines (using type over interface) and provides clear documentation for when each optional property is required.


215-269: LGTM!

The reset function implementation is well-designed:

  1. Early return for already-hashed keys avoids unnecessary recomputation
  2. Proper extraction of attached options from the catalog maintains API ergonomics
  3. Clear error messages when required context is missing for scoped keys
  4. Hash reconstruction uses the same algorithm as createIdempotencyKey ensuring consistency

The documentation provides comprehensive examples covering all use cases.

apps/webapp/test/runsReplicationService.part2.test.ts (2)

1295-1299: LGTM! Comprehensive test data for idempotency key options.

The test data correctly distinguishes between:

  • idempotencyKey: the hashed value stored for deduplication
  • idempotencyKeyOptions: the original user-provided key and scope

This aligns with the PR objective of exposing the user-provided idempotency key alongside the hash.


1395-1397: LGTM! Assertions verify complete idempotency key propagation.

The assertions correctly validate that all three idempotency-related fields are replicated to ClickHouse:

  • idempotency_key: the hashed value
  • idempotency_key_user: the original user-provided key
  • idempotency_key_scope: the scope ("run", "attempt", or "global")
packages/core/src/v3/idempotency-key-catalog/catalog.ts (1)

1-11: LGTM! Clean type definitions for idempotency key catalog.

The types are well-structured:

  • IdempotencyKeyScope correctly defines the three valid scopes per learnings
  • IdempotencyKeyOptions uses type instead of interface per coding guidelines
  • IdempotencyKeyCatalog interface provides a minimal, focused API for hash-based registration and lookup
apps/webapp/app/services/runsReplicationService.server.ts (2)

24-24: LGTM! Import uses proper subpath export.

The import from @trigger.dev/core/v3/serverOnly follows the coding guidelines for subpath imports.


895-896: LGTM! Idempotency key options correctly extracted and inserted.

The new fields use the unsafe extraction helpers which handle the untyped JSON appropriately. The ?? "" fallback ensures ClickHouse receives empty strings rather than undefined/null values.

Note: Per the relevant code snippets, unsafeExtractIdempotencyKeyScope returns a typed union "run" | "attempt" | "global" | undefined, providing implicit validation of the scope value.

packages/core/src/v3/idempotency-key-catalog-api.ts (1)

1-5: LGTM! Standard API entrypoint pattern.

The file follows the established pattern for module-level API exports (similar to apiClientManager-api.ts, task-context-api.ts), enabling tree-shaking while providing a convenient singleton access point.

packages/core/src/v3/utils/globals.ts (2)

4-4: LGTM! Type-only import prevents runtime side effects.

Using import type ensures this import is erased at compile time, avoiding unnecessary module loading.


65-65: LGTM! Global API surface extended for idempotency key catalog.

The field name "idempotency-key-catalog" correctly matches the API_NAME constant used in the catalog's index.ts file for registration/retrieval.

packages/core/src/v3/idempotency-key-catalog/index.ts (1)

7-36: LGTM! Well-designed singleton API with lazy initialization.

The implementation correctly:

  • Uses a private constructor with static getInstance() for singleton access
  • Lazily initializes the underlying LRU catalog on first access
  • Registers globally with allowOverride=true to permit catalog replacement if needed
  • Delegates all operations to the underlying catalog

The LRU-based default prevents unbounded memory growth from accumulated key registrations.

packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.test.ts (11)

6-13: Baseline store/retrieve case covered.


15-19: Non-existent key behavior is covered.


21-34: Multiple-key storage/retrieval is covered.


36-45: Re-register overwrite behavior is covered.


66-80: Eviction-on-capacity test looks good.


82-95: Multi-eviction scenario is covered.


97-106: MaxSize=1 behavior is covered.


110-127: Access-promotes-to-MRU scenario is covered.


129-146: Re-register-promotes-to-MRU scenario is covered.


148-163: Non-existent get does not affect order.


166-179: Default maxSize behavior is covered.

packages/core/src/v3/idempotency-key-catalog/lruIdempotencyKeyCatalog.ts (1)

26-34: LRU promotion on read is clear and correct.

apps/webapp/app/presenters/v3/SpanPresenter.server.ts (3)

11-14: Consistent use of user-key helpers across run payload and context.

Also applies to: 236-239, 678-679


285-307: Idempotency key status derivation is clear and well-ordered.


388-388: Selecting idempotencyKeyOptions ensures helper extraction has data.

packages/core/src/v3/serverOnly/idempotencyKeys.ts (5)

10-19: User-provided key extraction with fallback is solid.


27-35: Scope extraction with safe parsing looks good.


37-42: Unsafe scope extraction is concise and clear.


51-59: User-key-only extraction with safe parsing looks good.


61-67: Unsafe user-key extraction is concise and clear.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@ericallam ericallam merged commit 36168b3 into main Jan 20, 2026
55 of 56 checks passed
@ericallam ericallam deleted the feat/expose-user-idempotency-key-in-context branch January 20, 2026 11:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants